package com.fpost.manager;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.hibernate.HibernateException;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.Transaction;

import com.fpost.dao.impl.DictionaryDAO;
import com.fpost.domain.Article;
import com.fpost.domain.ArticleDictionary;
import com.fpost.domain.Dictionary;
import com.fpost.hibernate.util.HibernateSessionFactory;
import com.fpost.regex.util.RegexUtils;

public class SearchManager {
	
	//used in test
	public long fastCountCurrentArticlesByInputAndType(String input, long articleTypeId)  throws HibernateException {
		return fastGetCurrentArticleIdsByInputAndType(input, articleTypeId).size();
	}
	
	//used in search action
	public List<Long> fastGetCurrentArticleIdsByInputAndType(String input, long articleTypeId)  throws HibernateException {
		//List<Long> articleIds = getCurrentArticleIdsByInput(input);
		//List<Long> articleIds = getCurrentArticleIdsByInput_SQL_2(input);		
		List<Long> articleIds = getCurrentArticleIdsByInput_SQL_1_intersect(input);
		return new ArticleManager().selectArticleIdsByArticleTypeId_HQL(articleIds, articleTypeId);
	}
	
	@Deprecated
	public List<Article> fastGetCurrentArticlesByInputAndType(String input, long articleTypeId)  throws HibernateException {
		List<Long> articleIds = getCurrentArticleIdsByInput(input);
		List<Article> articles = new ArticleManager().getCurrentArticlesByArticleIds(articleIds);
		return selectArticlesByType(articles, articleTypeId);
	}
	
	@Deprecated
	//SQL with or w/o intersect is much faster then intersect implemented in java 
	private List<Long> getCurrentArticleIdsByInput(String input) throws HibernateException {
		
		List<String> articleEntries = RegexUtils.cleanInput(input);
		List<Dictionary> dictionaryList = new DictionaryDAO().getDictionaryList(articleEntries);
				 
		List<Long> articleIds = new ArrayList<Long>();
		if (dictionaryList.size() == 1) {				
			List<ArticleDictionary> articleDictionaryList = 
					new ArticleDictionaryManager().getArticleDictionaryByDictionaryId(dictionaryList.get(0).getDictionaryId());
			for (ArticleDictionary articleDictionary : articleDictionaryList) {
				articleIds.add(articleDictionary.getArticleId());
			}			
		} else if (dictionaryList.size() > 1) {
			articleIds = getDistinctArticleIdsFromSetsIntersection(dictionaryList);			
		}		
		return articleIds;		
	}
	
	public List<Long> getCurrentArticleIdsByInput_SQL_1_intersect(String input) throws HibernateException {
		
		List<String> articleEntries = RegexUtils.cleanInputUTF(input);
		StringBuilder sb = new StringBuilder();
		sb.append("select ad.article_id from article_dictionary ad, dictionary d ");
		sb.append("where ad.dictionary_id = d.dictionary_id ");
		sb.append("and d.entry = '" + articleEntries.get(0) + "' ");
		if (articleEntries.size() > 1) {		
			for (int i = 1; i < articleEntries.size(); i++) {
				sb.append("intersect ");
				sb.append("select ad.article_id from article_dictionary ad, dictionary d ");
				sb.append("where ad.dictionary_id = d.dictionary_id ");
				sb.append("and d.entry = '" + articleEntries.get(i) + "' ");
			} 
		}
		//System.out.println(sb.toString());
		
		Session session = HibernateSessionFactory.getInstance().getSessionFactory().openSession();
		Transaction tx = null;
		List<Object[]> ids = new ArrayList<Object[]>();
		try {
			tx = session.beginTransaction();
			SQLQuery query = session.createSQLQuery(sb.toString());				
			ids = (List<Object[]>)query.list();
			tx.commit();			
		} catch (HibernateException e) {
			tx.rollback();
			throw e;
		} finally {
			session.close();
		}
		List<Long> articleIds = new ArrayList<Long>();
		for (Object id : ids) {
			articleIds.add(((BigDecimal)id).longValue());
		} 
		return articleIds;		
	}
	
	/**
	 * performing intersect w/o intersect keyword (same as SQL_1_intersect)
	 * @param input
	 * @return
	 * @throws HibernateException
	 */
	@Deprecated
	public List<Long> getCurrentArticleIdsByInput_SQL_2(String input) throws HibernateException {
		
		List<String> articleEntries = RegexUtils.cleanInput(input);
		StringBuilder sb = new StringBuilder();
		sb.append("select ad.article_id from article_dictionary ad, dictionary d ");
		sb.append("where ad.dictionary_id = d.dictionary_id ");
		sb.append("and d.entry = '" + articleEntries.get(0) + "' ");
		if (articleEntries.size() > 1) {		
			for (int i = 1; i < articleEntries.size(); i++) {
				sb.append("and ad.article_id in ");
				sb.append("(select ad.article_id from article_dictionary ad, dictionary d ");
				sb.append("where ad.dictionary_id = d.dictionary_id ");
				sb.append("and d.entry = '" + articleEntries.get(i) + "') ");
			} 
		}
		//System.out.println(sb.toString());
		
		Session session = HibernateSessionFactory.getInstance().getSessionFactory().openSession();
		Transaction tx = null;
		List<Object[]> ids = new ArrayList<Object[]>();
		try {
			tx = session.beginTransaction();
			SQLQuery query = session.createSQLQuery(sb.toString());				
			ids = (List<Object[]>)query.list();
			tx.commit();			
		} catch (HibernateException e) {
			tx.rollback();
			throw e;
		} finally {
			session.close();
		}
		List<Long> articleIds = new ArrayList<Long>();
		for (Object id : ids) {
			articleIds.add(((BigDecimal)id).longValue());
		} 
		return articleIds;		
	}
	
	private List<Article> selectArticlesByType(List<Article> all, long articleTypeId) {
		List<Article> selected = new ArrayList<Article>();
		if (articleTypeId == 0) {			
			selected = all;
		} else {
			for (Article article : all) {
				if (article.getVersions().get(0).getArticleTypeId() == articleTypeId) {
					selected.add(article);	
				}						
			}
		}
		return selected;
	}
	
	private List<Long> getDistinctArticleIdsFromSetsIntersection(List<Dictionary> dictionaryList) throws HibernateException {
		
		List<List<ArticleDictionary>> lists = new ArrayList<List<ArticleDictionary>>();
		ArticleDictionaryManager articleDictionaryManager = new ArticleDictionaryManager();
		for (Dictionary dictionary : dictionaryList) {		
			lists.add(articleDictionaryManager.getArticleDictionaryByDictionaryId(dictionary.getDictionaryId()));
		}
		
		boolean changed = false;
		Set<Long> resultSet = new HashSet<Long>();
		List<ArticleDictionary> articleDictionaryList = new ArrayList<ArticleDictionary>(lists.get(0));
		for (ArticleDictionary articleDictionary : articleDictionaryList) {
			resultSet.add(articleDictionary.getArticleId());
		}				
		int resultSetInitialSize = resultSet.size();
		for (int i = 1; i < lists.size(); i++) {	
			Set<Long> temp = new HashSet<Long>();
			articleDictionaryList = new ArrayList<ArticleDictionary>(lists.get(i));
			for (ArticleDictionary articleDictionary : articleDictionaryList) {
				temp.add(articleDictionary.getArticleId());
			}			
			changed = resultSet.retainAll(temp);			
		}
		if (!changed) {
			assert resultSetInitialSize == resultSet.size();
		} 
		return new ArrayList<Long>(resultSet);		
	}
	
	@Deprecated
	public long fastCountCurrentArticlesByInput(String input) throws HibernateException {
		
		List<String> articleEntries = RegexUtils.cleanInput(input);
		List<Dictionary> dictionaryList = new DictionaryDAO().getDictionaryList(articleEntries);
		
		long result = 0L;
		if (dictionaryList.size() == 1) {				
			List<ArticleDictionary> articleDictionaryList = 
					new ArticleDictionaryManager().getArticleDictionaryByDictionaryId(dictionaryList.get(0).getDictionaryId());
			result = articleDictionaryList.size();
		} else if (dictionaryList.size() > 1) {
			result = getDistinctArticleIdsFromSetsIntersection(dictionaryList).size();
		}
		return result;
	}

}
